#include "Md2Model.h"
#include <stdio.h>
#include "../helper/Helper.h"

Md2Model::Md2Model()
{
	header = 0;
	textureUV = 0;
	faces = 0;
	frameHeader = 0;
	frames = 0;
	
	fVertices = 0;
	fTextures = 0;
	
	curTimeElapsed = 0;
	frameTimeElapsed = 200;
}

Md2Model::~Md2Model()
{
	Cleanup();
}

void Md2Model::Load(const char* file) {
	FILE* f;
	int i, j, k;
	f = fopen(file, "r");
	/* header */
	header = new Md2Header;
	fread(header, sizeof(int), 17, f);
	/* allocate space for vertices and textureUV */
	textureUV = new Md2TextureCoordinate[header->numVertices];
	faces = new Md2Face[header->numFaces];
	frameHeader = new Md2FrameHeader[header->numFrames]; //each frame has a frame header
	frames = new Md2Frame[header->numFrames];
	for (i = 0; i < header->numFrames; i++)
		frames[i].vertices = new Md2Vertex[header->numVertices];
	
	/* texture name, 64 bytes, not used */
	char textureName[65];
	memset(textureName, 0, 65);
	fseek(f, header->offTextures, SEEK_SET);
	fread(textureName, sizeof(byte), 64, f);
	
	/* texture coordinate */
	fseek(f, header->offTexCoords, SEEK_SET);
	fread(textureUV, sizeof(Md2TextureCoordinate), header->numVertices, f);
	
	/* faces */
	fseek(f, header->offFace, SEEK_SET);
	fread(faces, sizeof(Md2Face), header->numFaces, f);
	
	/* frames */
	fseek(f, header->offFrame, SEEK_SET);
	for (i = 0; i < header->numFrames; i++) {
		/* frames header */
		fread(&frameHeader[i], sizeof(Md2FrameHeader), 1, f);
		/* frames data */
		fread(frames[i].vertices, sizeof(Md2Vertex), header->numVertices, f);
	}
	/* decompress the whole vertices and texture coordinates */
	/* vertices */
	fVertices = new Vector3f[header->numFrames*header->numVertices];
	k = 0;
	for (i = 0; i < header->numFrames; i++) {
		for (j = 0; j < header->numVertices; j++) {
			fVertices[k++] = Vector3f(
				frames[i].vertices[j].compressedVertex[0] * frameHeader[i].scale[0] + frameHeader[i].translate[0],
				frames[i].vertices[j].compressedVertex[1] * frameHeader[i].scale[1] + frameHeader[i].translate[1],
				frames[i].vertices[j].compressedVertex[2] * frameHeader[i].scale[2] + frameHeader[i].translate[2]
			);
		
		}
	}
	
	/* textures */
	fTextures = new Vector3f[header->numFrames*header->numVertices];
	for (i = 0; i < header->numVertices; i++)
		fTextures[i] = Vector3f(
			(float) textureUV[i].compressedU / header->texWidth,
			(float) textureUV[i].compressedV / header->texHeight,
			0
		);
		
	/* output something */
	printf("Vertices: %d\n", header->numVertices);
	printf("Frames: %d\n", header->numFrames);
	printf("Frame size: %d\n", header->frameSize);
	printf("Texture name: %s\n", textureName);
	printf("offTextures: %d\n", header->offTextures);
	printf("offTexCoords: %d\n", header->offTexCoords); //offset to texture coordinates
	printf("offFace: %d\n", header->offFace); //offset to triangle list
	printf("offFrame: %d\n", header->offFrame);
	printf("Vertices[0]: %s\n", fVertices[0].ToString());
	
	fclose(f);
	
	
	
}

void Md2Model::LoadTexture(const char* file) {
	/* texture loading */
	SDL_Surface* surface = Helper::LoadTextureImage(file);
	
	GLint iWidth = surface->w;
	GLint iHeight = surface->h;
	GLenum eFormat;
	eFormat = GL_RGB; //texture format
	//eFormat = GL_COLOR;
	printf("bpp: %d\n", surface->format->BytesPerPixel);
	SDL_Color* palette = surface->format->palette->colors;
	SDL_Color color;
	int bpp = 3;
	byte* pixels = new byte[iWidth*iHeight*bpp];
	byte* surfaceIndex = (byte*)surface->pixels;
	int i, j, index;
	for (i = 0; i < iWidth; i++) 
		for (j = 0; j < iHeight; j++) {
			index = i*iWidth+j;
			color = palette[surfaceIndex[index]];
			pixels[index*bpp+0] = color.r;
			pixels[index*bpp+1] = color.g;
			pixels[index*bpp+2] = color.b;
		}
	
	//Generate texture objects
	glGenTextures(MD2_TEXTURE_COUNT, textures);
	//glPixelMapuiv(GL_PIXEL_MAP_I_TO_R, surface->map->);
	glBindTexture(GL_TEXTURE_2D, textures[0]); //bind texture index
	glTexImage2D(GL_TEXTURE_2D, 0 /* mipmap */, GL_RGB /* internal image format */, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pixels);
	
	//Texture setup
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	
	SDL_FreeSurface(surface);
	ReleaseNewArray(pixels);
}

void Md2Model::NextFrame() {
	if (header) 
		curFrame = (curFrame + 1) % header->numFrames;
}

void Md2Model::Update(long time) {
	curTimeElapsed += time;
	if (curTimeElapsed >= frameTimeElapsed) {
		NextFrame();
		curTimeElapsed -= frameTimeElapsed;
	}
}

void Md2Model::Render() {
	int i, j;
	int offFrame = curFrame * header->numVertices;
	int nextFrame = (curFrame + 1) % header->numFrames;
	int offNextFrame = nextFrame * header->numVertices;
	
	/* md2model's front face is clock-wise order. */
	glPushAttrib(GL_FRONT_FACE);
		glFrontFace(GL_CW); 
		
		/* draw key frames */
		/*glBegin(GL_TRIANGLES);
			for (i = 0; i < header->numFaces; i++) {
				for (j = 0; j < 3; j++) {
					glTexCoord2f(
						fTextures[offFrame + faces[i].indexTexture[j]].x, 
						fTextures[offFrame + faces[i].indexTexture[j]].y 
					);
					glVertex3f(
						fVertices[offFrame + faces[i].indexVertex[j]].x,
						fVertices[offFrame + faces[i].indexVertex[j]].y, 
						fVertices[offFrame + faces[i].indexVertex[j]].z
					);
				}
			}
		glEnd();*/
		
		/* with linear interpolation between key frames */
		float curX, curY, curZ, nextX, nextY, nextZ, x, y, z;
		glBindTexture(GL_TEXTURE_2D, textures[0]);
		glBegin(GL_TRIANGLES);
			for (i = 0; i < header->numFaces; i++) {
				for (j = 0; j < 3; j++) {
					glTexCoord2f(
						fTextures[faces[i].indexTexture[j]].x, 
						fTextures[faces[i].indexTexture[j]].y 
					);
					
					curX = fVertices[offFrame + faces[i].indexVertex[j]].x;
					curY = fVertices[offFrame + faces[i].indexVertex[j]].y; 
					curZ = fVertices[offFrame + faces[i].indexVertex[j]].z;
					
					nextX = fVertices[offNextFrame + faces[i].indexVertex[j]].x;
					nextY = fVertices[offNextFrame + faces[i].indexVertex[j]].y; 
					nextZ = fVertices[offNextFrame + faces[i].indexVertex[j]].z;
					
					x = curX + (nextX - curX) * curTimeElapsed / frameTimeElapsed;
					y = curY + (nextY - curY) * curTimeElapsed / frameTimeElapsed;
					z = curZ + (nextZ - curZ) * curTimeElapsed / frameTimeElapsed;
					glVertex3f(
						x, y, z
					);
				}
			}
		glEnd();
	
	/* restore to default face order (counter clock-wise) */
	glPopAttrib();
}

void Md2Model::Cleanup() {
	ReleaseNew(header);
	ReleaseNewArray(textureUV);
	ReleaseNewArray(faces);
	ReleaseNewArray(frames);
	ReleaseNewArray(frameHeader);
	
	ReleaseNewArray(fVertices);
	ReleaseNewArray(fTextures);
	
}
